
#pragma once

#include "gl/glew.h"
#include "mymath.h"
#include "ublocks.h"
#include "technique.h"

#include <vector>
#include <queue>

using namespace std;

//////////////////////////////////////////////////////////////////////////
// Some defines
#define MAX_HEADERS 10

//////////////////////////////////////////////////////////////////////////
// Mesh related
enum MeshPresentMethod {
    PRESENT_SIMPLE,
    PRESENT_TESSELATED
};

class IMesh {
    char* file2string(const char *path);
protected:
    int CompileAndPrintLog(GLuint shader, const char* source, const char* preamb, int headers=0, ...);
    int LinkAndPrintLog(GLuint program);

public:
//     IMesh() {}
    virtual ~IMesh() {}
    virtual void Draw(const PresentTechnique* pTechnique)=0;
    virtual void AttachUniforms(const UniformMap& uBlocks)=0;
    virtual void MapAttributes(const ProgramsMap& tMap)=0;
    virtual MeshPresentMethod PresentMethod() const=0;

    // m_ModelViewMatrix should be 16 aligned.
    // Since it comes first (after the pointer to vftable), a good compiler should preserve alignment ;)
    void* operator new(size_t size);
    void operator delete(void* addr);
};

//////////////////////////////////////////////////////////////////////////
// Scene related
class Scene {
    vec4                    m_qRot;
    vec4                    m_qRotTheta;
    vec4                    m_Trans;
    vec4                    m_lightsPos[MAX_LIGHTS];
    LightsBlock             m_lights;
    MatrixBlock             m_matrices;
    MatrixBlock             m_inverseMatrices;
    ModelBlock              m_modelStuff;
    MaterialBlock           m_material;
    
    map<MeshPresentMethod, vector<IMesh*>>      m_meshes;
    map<MeshPresentMethod, PresentTechnique*>   m_drawEffects;
    
    GLsizei                 m_width, m_height;
    UniformMap              m_uniformBlocks;

    ProgramsMap             m_effectMap;

    float                   m_near,m_far,m_fov,m_ar;
    float                   m_phi,m_theta;
    bool                    m_lockToY;

    void CreateCameraMatrix();
public:
    Scene();
    ~Scene();
    void* operator new(size_t size);
    void operator delete(void* addr);

    void Reshape(int width, int height);
    void Translate(float dx, float dy, float dz);
    void Scale(float magnitude);
    void RotateAroundAxis(float x, float y, float z, float angle);
    void RotateViewBy(float dphi, float dtheta);

    void Render(float dt);
    void AttachMesh(IMesh* mesh);

    void SelectEvent(int x, int y);

    void AttachUniformBlock(const UniformBlock* uBlock);
};
